use std::collections::HashMap;
use std::fmt::Formatter;
use crate::svd::fraction::{Fraction, ToFraction};

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub(crate) enum Operator {
    Add,
    Sub,
    Mul,
    Div,
    Neg,
    Rec,
}

impl std::fmt::Display for Operator {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Operator::Add => { write!(f, "+") }
            Operator::Sub => { write!(f, "-") }
            Operator::Mul => { write!(f, "*") }
            Operator::Div => { write!(f, "/") }
            Operator::Neg => { write!(f, "-") }
            Operator::Rec => { write!(f, "/") }
        }
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FractionResult {
    operator: Operator,
    pub fraction: Fraction,
    fst: Fraction,
    snd: Fraction,
}

impl FractionResult {
    fn new(operator: Operator, fraction: Fraction, fst: Fraction, snd: Fraction) -> FractionResult {
        FractionResult { operator, fraction, fst, snd }
    }
    pub(crate) fn cal(a: Fraction, b: Fraction) -> Vec<FractionResult> {
        let mut res = vec![Self::new(Operator::Add, a + b, a, b),
                           Self::new(Operator::Sub, a - b, a, b),
                           Self::new(Operator::Mul, a * b, a, b),
                           Self::new(Operator::Neg, b - a, a, b),
        ];
        if b != (0, 1).f() {
            res.push(Self::new(Operator::Div, a / b, a, b))
        }
        if a != (0, 1).f() {
            res.push(Self::new(Operator::Rec, b / a, a, b))
        }
        res
    }

    pub(crate) fn print_fraction_result(f: &[FractionResult]) ->String{
        let mut map: HashMap<Fraction, String> = HashMap::new();
        let mut res_str: String = String::new();
        let mut i1;
        let mut i2;
        for x in f.iter() {
            if map.contains_key(&x.fst) {
                i1 = map.get(&x.fst).unwrap().clone();
                map.remove(&x.fst);
            } else {
                i1 = x.fst.to_int().unwrap().to_string();
            }
            if map.contains_key(&x.snd) {
                i2 = map.get(&x.snd).unwrap().clone();
                map.remove(&x.snd);
            } else {
                i2 = x.snd.to_int().unwrap().to_string();
            }

            match x.operator {
                Operator::Neg => { res_str = format!("({}{}{})", i2, x.operator, i1) }
                Operator::Rec => { res_str = format!("({}{}{})", i2, x.operator, i1) }
                _ => { res_str = format!("({}{}{})", i1, x.operator, i2) }
            }
            map.insert(x.fraction, res_str.clone());
        }
        res_str
    }
}